fix(apptests): detect locked desktop / disconnected session#164
Conversation
E2E runs against WinAppDriver fail every Click() when the workstation locks (idle timeout, manual lock, RDP disconnect) — surfacing as a flood of WebDriverException "An unknown error occurred in the remote end" that's indistinguishable from real test flake. Observed in repeated runs where 5+ consecutive iterations failed 48/57 tests with identical stack traces, then recovered on next iteration once the desktop was unlocked. Add SessionInteractivityGuard to query OpenInputDesktop (locked screen switches the input desktop from "Default" to "Winlogon") and WTSConnectState (RDP/console connect state). On detection: - Emit Assert.Inconclusive instead of letting the test Fail, so the .trx outcome distinguishes environmental from real failures. - Write a marker file at \$E2E_LOCK_MARKER_PATH so external runners can abort the rest of a multi-iteration loop instead of generating more false-negative results. Hooks: [TestInitialize] in AppTestBase preflights every test, plus a WebDriverException recheck in NavigateToFixture and TestSession session bootstrap to catch locks that happen mid-operation. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR improves the reliability and diagnosability of tests/Reactor.AppTests runs by detecting non-interactive desktop conditions (locked workstation / disconnected session) and converting resulting WinAppDriver failures into MSTest Inconclusive, with an optional marker file to let external runners halt further iterations.
Changes:
- Add
SessionInteractivityGuardto detect locked/disconnected desktop state (OpenInputDesktop + WTS connect state), write a marker file, and throwAssert.Inconclusive. - Preflight interactivity before each test via
[TestInitialize]inAppTestBase. - Recheck interactivity on
WebDriverExceptionduring navigation (NavigateToFixture) and duringTestSessionbootstrap.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| tests/Reactor.AppTests/Infrastructure/TestSession.cs | Adds interactivity preflight and lock-aware recheck around WinAppDriver session bootstrap. |
| tests/Reactor.AppTests/Infrastructure/SessionInteractivityGuard.cs | New guard for detecting locked/disconnected sessions, emitting Inconclusive, and writing a marker file. |
| tests/Reactor.AppTests/Infrastructure/AppTestBase.cs | Adds per-test interactivity preflight and recheck on WebDriver failures during fixture navigation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- AppTestBase: drop dead "final attempt" line + misleading comment. The for-loop's `when (attempt == 0)` filter meant iteration 1's WebDriverTimeoutException always propagated out, making the post-loop WaitForText unreachable. - SessionInteractivityGuard: only treat OpenInputDesktop returning NULL as Locked when GetLastWin32Error is ERROR_ACCESS_DENIED. Other failure codes are Unknown and don't trigger Inconclusive — avoids masking real test failures behind transient Win32 errors. - SessionInteractivityGuard: WriteMarker uses FileMode.CreateNew for an atomic first-writer-wins, replacing the racy File.Exists+WriteAllText pattern. Stale markers still won't get overwritten silently. - TestSession: extend the bootstrap try/catch to cover WaitForHostWindow too, and handle TimeoutException as well as WebDriverException — a mid-init lock surfaces as a TimeoutException from the polling loop, not as a WebDriverException. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Addressed all four review comments in
Build clean. |
Summary
Click()when the workstation locks (idle timeout, manual lock, RDP disconnect), surfacing as a flood ofWebDriverException "An unknown error occurred in the remote end"that's indistinguishable from real test flake. Observed in a recent 50-run loop where 5 consecutive iterations failed 48/57 tests with identical stack traces, then recovered the moment the desktop was unlocked.SessionInteractivityGuard(P/Invoke toOpenInputDesktop+WTSConnectState). Locked screens switch the input desktop fromDefaulttoWinlogon; RDP disconnect changesWTSConnectStatetoWTSDisconnected. Both are deterministic and cheap.Assert.Inconclusive(not Fail) so the .trx outcome separates environmental from real failures, and write a marker file at$E2E_LOCK_MARKER_PATHso external multi-run loops can stop scheduling further iterations instead of generating more false negatives.Hook points:
[TestInitialize]inAppTestBase— preflights every test method.try/catch (WebDriverException)inNavigateToFixture— rechecks; lock → Inconclusive, otherwise rethrow as a real failure.TestSessionsession bootstrap so a class-init lock bails out cleanly without spending 30s booting WinAppDriver into a doomed run.Test plan
dotnet build tests/Reactor.AppTestsdotnet test tests/Reactor.AppTests→ 57/57 pass, no marker written.dotnet test, lock the workstation mid-run, verify subsequent tests are reportedInconclusive(notFailed) and the marker file is written.dotnet test, verifyTestSession.AssemblyInitshort-circuits before launching WinAppDriver/Host.🤖 Generated with Claude Code